import { Injectable } from '@angular/core'
import { Observable, of, forkJoin, throwError } from 'rxjs'
import { concatMap, map } from 'rxjs/operators'
import { EPaymentBrand } from '@shared/enums'
import { FieldBase } from './field-base.interface'
import { HiddenField } from './field-hidden.interface'
import { GroupField } from './field-group.interface'
import {
  IQRRouteOfStore,
  IStoreChanMerAlipay,
  IStoreChanMerWeChat,
  IStoreChanMerUPI,
  IStoreCardChanMer,
  IStoreCardChanMerMCC,
  ICardRouteOfStore,
  IRouteOfStore,
  MetaData,
  IStoreCardChanMerAME,
  IECRouteOfStore,
  IStoreECChanMer,
  IStoreChanMerTRU,
} from '@shared/interfaces/store'
import { ChanMerSelectField } from './field-chan-mer-select.interface'
import { SelectField } from './field-select.interface'
import { StoreService } from '../../store.service'
import { Currency } from '@shared/interfaces/common'
import { numScale } from 'app/utils/helpers'
import { ChannelFormQRService } from './channel-form-qr.service'
import { ChannelFormCardService } from './channel-form-card.service'
import { ChannelFormECService } from './channel-form-ec.service'
import { EChannelsSwitcherCategory } from '../channels-switcher/channels-switcher.interface'
import { QrChannel } from '@shared/interfaces/acquirer'
import { IStoreChannelFormOutput } from '../../new/qrcode-channel/qrcode-channel.interface'
import { IStoreEcChannelFormOutput } from '../../new/ec-channel/ec-channel.interface'
import { ECategory } from '@shared/interfaces/trade'
import { DataService } from '@shared/services/data.service'
import { NzMessageService } from 'ng-zorro-antd'
import { I18NService } from '@core'
@Injectable()
export class ChannelFormService {
  constructor(
    private storeService: StoreService,
    private cardService: ChannelFormCardService,
    private qrService: ChannelFormQRService,
    private ecService: ChannelFormECService,
    private dataService: DataService,
    public msg: NzMessageService,
    private i18n: I18NService,
  ) {}

  convertToIRouteOfStore(category: EChannelsSwitcherCategory, input: IStoreChannelFormOutput): IRouteOfStore {
    if (category === EChannelsSwitcherCategory.QR) {
      return this.convertToIQRRouteOfStore(input)
    }

    if (category === EChannelsSwitcherCategory.Card) {
      return this.convertToICardRouteOfStore(input)
    }

    if (category === EChannelsSwitcherCategory.EC) {
      return this.convertToIECRouteOfStore(input)
    }

    return null
  }

  // convert route data from type of IStoreChannelFormOutput to type of IQRRouteOfStore
  // IStoreChannelFormOutput is the type of raw data inside validateForm
  convertToIQRRouteOfStore(input: IStoreChannelFormOutput): IQRRouteOfStore {
    const route: IQRRouteOfStore = {
      chanCode: input.chanCode,
      paymentBrand: input.paymentBrand,
      mcc: input.mcc,
      currency: input.currency,
      chanMerObjId: '',
      category: EChannelsSwitcherCategory.QR,
    }

    if (typeof input.fee === 'number' && !Number.isNaN(input.fee)) {
      route.fee = numScale(input.fee, -2)
    }

    switch (input.paymentBrand) {
      case EPaymentBrand.Alipay:
        const cma: IStoreChanMerAlipay = {
          PID: input.chanMerParams.PID.chanMerID,
          secondaryMerchantID: input.chanMerParams.secondaryMerchantID,
          secondaryMerchantName: input.chanMerParams.secondaryMerchantName,
          storeID: input.chanMerParams.storeID,
          storeName: input.chanMerParams.storeName,
        }
        route.chanMerObjId = input.chanMerParams.PID.id
        route.chanMerParams = cma
        break
      case EPaymentBrand.WeChat:
        const cmw: IStoreChanMerWeChat = {
          merchantID: input.chanMerParams.merchantID.chanMerID,
          subMerchantID: input.chanMerParams.subMerchantID,
        }
        route.chanMerObjId = input.chanMerParams.merchantID.id
        route.chanMerParams = cmw
        break
      case EPaymentBrand.UPI:
        const cmu: IStoreChanMerUPI = {
          acquiringIIN: input.chanMerParams.acquiringIIN.chanMerID,
          forwardingIIN: input.chanMerParams.acquiringIIN.chanMerIDSecondary,
        }
        route.chanMerObjId = input.chanMerParams.acquiringIIN.id
        route.chanMerParams = cmu
        break
      case EPaymentBrand.TrueMoney:
        const tru: IStoreChanMerTRU = {
          shopID: input.shopID,
          payNearby: input.payNearby === '' ? false : input.payNearby,
          authOnline: input.authOnline === '' ? false : input.authOnline
        }
        route.chanMerID = input.chanMerID as string
        route.chanMerParams = tru
        break
      default:
        break
    }

    return route
  }

  // convert route data from type of IStoreChannelFormOutput to type of ICardRouteOfStore
  // IStoreChannelFormOutput is the type of raw data inside validateForm
  private convertToICardRouteOfStore(input: IStoreChannelFormOutput): ICardRouteOfStore {
    const route: ICardRouteOfStore = {
      chanCode: input.chanCode,
      paymentBrand: input.paymentBrand,
      mcc: input.mcc,
      currency: input.currency,
      chanMerObjId: '',
      first3DigitsOfSENumber: input.first3DigitsOfSENumber,
      chanNameEN: input.chanNameEN,
      chanAddrEN: input.chanAddrEN,
      ain: input.ain,
      category: EChannelsSwitcherCategory.Card,
    }

    if (typeof input.fee === 'number' && !Number.isNaN(input.fee)) {
      route.fee = numScale(input.fee, -2)
    }
    switch (input.paymentBrand) {
      case EPaymentBrand.Visa:
        const cmv: IStoreCardChanMer = {
          offlineInsCode: input.chanMerParams.selectedChanMer.offlineInsCode,
          bin: input.chanMerParams.selectedChanMer.bin,
        }
        route.chanMerObjId = input.chanMerParams.selectedChanMer.id
        route.chanMerParams = cmv
        break
      case EPaymentBrand.MasterCard:
        const cmm: IStoreCardChanMerMCC = {
          offlineInsCode: input.chanMerParams.selectedChanMer.offlineInsCode,
          bin: input.chanMerParams.selectedChanMer.bin,
          ica: input.chanMerParams.selectedChanMer.ica,
        }
        route.chanMerObjId = input.chanMerParams.selectedChanMer.id
        route.chanMerParams = cmm
        break
      case EPaymentBrand.AME:
        const ame: IStoreCardChanMerAME = {
          ain: input.chanMerParams.selectedChanMer.ain,
          first3DigitsOfSENumber: input.chanMerParams.selectedChanMer.first3DigitsOfSENumber,
        }
        route.sponsorMerCode = input.sponsorMerCode
        route.chanMerObjId = input.chanMerParams.selectedChanMer.id
        route.chanMerParams = ame
        break
      case EPaymentBrand.UPI:
        const upi: IStoreCardChanMerAME = {
          ain: input.chanMerParams.selectedChanMer.ain,
          fin: input.chanMerParams.selectedChanMer.fin,
        }
        route.chanMerObjId = input.chanMerParams.selectedChanMer.id
        route.chanMerParams = upi
        break
      default:
        break
    }

    return route
  }

  // convert route data from type of IStoreChannelFormOutput to type of IQRRouteOfStore
  // IStoreChannelFormOutput is the type of raw data inside validateForm
  convertToIECRouteOfStore(input: IStoreChannelFormOutput): IECRouteOfStore {
    const route: IECRouteOfStore = {
      chanCode: input.chanCode,
      paymentBrand: input.paymentBrand,
    }

    if (typeof input.fee === 'number' && !Number.isNaN(input.fee)) {
      input.fee = numScale(input.fee, -2)
    }

    switch (input.paymentBrand) {
      case EPaymentBrand.UPI:
        const ecPay: IStoreECChanMer = {
          upiAcquiringIIN: input.chanMerParams.selectedChanMer.acquiringIIN,
          upiMerchantID: input.upiMerchantID,
          upiMerchantAbbreviation: input.upiMerchantAbbreviation,
          fee: input.fee,
        }
        route.chanMerObjId = input.chanMerParams.selectedChanMer.id
        route.chanMerParams = ecPay
        break

      default:
        break
    }

    return route
  }

  getFieldsAsync(
    category: EChannelsSwitcherCategory,
    brand: string,
    insCode: string,
    storeCurrencies: Currency[],
    value?: IQRRouteOfStore,
    metaData?: MetaData,
  ): Observable<FieldBase<any>[]> {
    let channels$: Observable<QrChannel[]>

    if (category === 'qr') {
      channels$ = this.storeService.getAcquirerQRChannels(insCode)
    } else if (category === 'card') {
      channels$ = this.storeService.getAcquirerCardChannels(insCode)
    } else if (category === 'ec') {
      channels$ = this.storeService.getAcquirerECChannels(insCode)
    } else {
      return throwError('unsupported category')
    }
    const fields = this.getFields(category, brand, value, metaData)
    return forkJoin(channels$, this.currencyOptionsOfStore$(insCode, storeCurrencies)).pipe(
      map(result => {
        const [channels, currencies] = result
        let idx = -1
        for (const [index, field] of fields.entries()) {
          if (field instanceof GroupField) {
            for (const insideField of field.fields) {
              if (insideField instanceof ChanMerSelectField) {
                insideField.options = channels.filter(ch => {
                  const paymentBrand = ch.paymentBrand || ch.chanCode
                  return paymentBrand === brand
                })
                break
              }
            }
          }

          if (field.key === 'currency' && field instanceof SelectField) {
            field.options = currencies
            if (!currencies.length) idx = index
          }
        }
        if (idx !== -1) fields.splice(idx, 1)
        return fields
      }),
    )
  }

  private getFields(
    category: EChannelsSwitcherCategory,
    brand: string,
    value?: IRouteOfStore,
    metaData?: MetaData,
  ): FieldBase<any>[] {
    let fields: FieldBase<any>[] = []
    switch (category) {
      case EChannelsSwitcherCategory.QR:
        fields = this.getFieldsOfQR(brand, value)
        break
      case EChannelsSwitcherCategory.Card:
        fields = this.getFieldsOfCard(brand, value, metaData)
        break
      case EChannelsSwitcherCategory.EC:
        fields = this.getFieldsOfEC(brand, value)
        break
      default:
        break
    }
    fields.push(
      ...[
        new HiddenField({
          key: 'chanCode',
          label: '',
          value: brand,
          order: 0,
        }),
        new HiddenField({
          key: 'paymentBrand',
          label: '',
          value: brand,
          order: 0,
        }),
        new HiddenField({
          key: 'sponsorMerCode',
          label: '',
          value: value.sponsorMerCode,
          order: 0,
        }),
        // new HiddenField({
        //   key: 'checked',
        //   label: '',
        //   value: true,
        //   order: -1,
        // }),
      ],
    )
    return fields.sort((a, b) => a.order - b.order)
  }

  private getFieldsOfQR(brand: string, value?: IQRRouteOfStore): FieldBase<any>[] {
    let fields: FieldBase<any>[] = []

    switch (brand) {
      case EPaymentBrand.Alipay:
        fields = this.qrService.getAlipayChannelFields(value)
        break
      case EPaymentBrand.WeChat:
        fields = this.qrService.getWeChatPayChannelFields(value)
        break
      case EPaymentBrand.TrueMoney:
        fields = this.qrService.getTrueMoneyChannelFields(value)
        break
      case EPaymentBrand.UPI:
        fields = this.qrService.getUPIChannelFields(value)
        break
      // case EPaymentBrand.Visa:
      //   fields = this.qrService.getVisaChannelFields(value)
      //   break
      // case EPaymentBrand.MasterCard:
      //   fields = this.qrService.getMasterCardChannelFields(value)
      //   break
      // case EPaymentBrand.FPS:
      //   fields = this.qrService.getFPSChannelFields(value)
      //   break
      default:
        break
    }
    return fields
  }

  private getFieldsOfCard(brand: string, value?: ICardRouteOfStore, metaData?: MetaData): FieldBase<any>[] {
    let fields: FieldBase<any>[] = []

    switch (brand) {
      case EPaymentBrand.Visa:
        fields = this.cardService.getVisaChannelFields(value)
        break
      case EPaymentBrand.MasterCard:
        fields = this.cardService.getMasterCardChannelFields(value)
        break
      case EPaymentBrand.AME:
        fields = this.cardService.getAmexChannelFields(value, metaData)
        break
      case EPaymentBrand.UPI:
        fields = this.cardService.getUpiChannelFields(value, metaData)
        break
      default:
        break
    }
    return fields
  }

  private getFieldsOfEC(brand: string, value?: IECRouteOfStore): FieldBase<any>[] {
    let fields: FieldBase<any>[] = []

    switch (brand) {
      case EPaymentBrand.UPI:
        fields = this.ecService.getUPIChannelFields(value)
        break
      default:
        break
    }

    return fields
  }

  // 渠道中的币种选项是门店选择的币种与币种转换关系计算出来的结果
  private currencyOptionsOfStore$(insCode: string, storeCurrencies: Currency[]): Observable<string[]> {
    return of(storeCurrencies).pipe(
      concatMap(currencies => {
        const m = new Map<string, string>()
        for (const c of currencies) {
          m.set(c.code, c.code)
        }

        return this.storeService.getAcquirerExchangeRates(insCode).pipe(
          map(rates => {
            if (!rates.length) return []
            rates.filter(r => m.has(r.srcCurrency)).forEach(r => m.set(r.dstCurrency, r.dstCurrency))
            return Array.from(m.keys())
          }),
        )
      }),
    )
  }


  trans(key: string, params?: Object): string {
    return this.i18n.fanyi(key, params)
  }
}
